using System;
using System.Threading;

using Microsoft.SPOT;

using SecretLabs.NETMF.Hardware;
using SecretLabs.NETMF.Hardware.NetduinoPlus;

namespace Coding4Fun.Maelstrom.Hardware.Modules
{
	public static class RgbStripLighting
	{
		private static readonly PWM RedPwm = new PWM(Pins.GPIO_PIN_D5);
		private static readonly PWM GreenPwm = new PWM(Pins.GPIO_PIN_D6);
		private static readonly PWM BluePwm = new PWM(Pins.GPIO_PIN_D9);

		private const int ThreadSleep = 50;
		private const int MaxValue = 100;

		const int PulseGreenIncrement = 2;
		const int PulseGreenThreadSleep = 100;

		const int PulsePurpleIncrement = 2;
		const int PulsePurpleThreadSleep = 100;

		private static Thread _animationThread;
		private static bool _killThread;

		#region game state animations
		public static void PlayGameIdle()
		{
			AbortAnimationThread();

			_animationThread = new Thread(PulsePurple);
			_animationThread.Start();
		}

		public static void PlayGameResultsLost()
		{
			AbortAnimationThread();

			_animationThread = new Thread(() => PulseRed(.8, false));
			_animationThread.Start();
		}

		public static void PlayGameResultsWin()
		{
			AbortAnimationThread();

			_animationThread = new Thread(() => PulseGreen(PulseGreenIncrement, false));
			_animationThread.Start();
		}
		#endregion

		public static void StopAnimation()
		{
			AbortAnimationThread();
		}

		public static void PlayScorePulse()
		{
			AbortAnimationThread();

			_animationThread = new Thread(() => PulseGreen(10));
			_animationThread.Start();
		}

		public static void PlayDamagePulse(double normalizedHealth)
		{
			// verifying bounds
			if (normalizedHealth < 0 || normalizedHealth > 1)
				return;

			AbortAnimationThread();

			// per rick, he perferred same speed, .2

			_animationThread = new Thread(() => PulseRed(.2));
			_animationThread.Start();
		}

		
		private static void PulsePurple()
		{
			while (!_killThread)
			{
				for (var i = 0; i <= 50; i += PulsePurpleIncrement)
				{
					SetPwmRgb(i, 0, i);
				}

				for (var i = 50; i >= 0; i -= PulsePurpleIncrement)
				{
					SetPwmRgb(i, 0, i);
				}

				Thread.Sleep(PulsePurpleThreadSleep);
			}
		}

		private static void PulseGreen(int increment, bool autoKillThread = true)
		{
			var index = 0;

			while (!_killThread)
			{
				for (var i = 0; i <= 50; i += increment)
				{
					SetPwmRgb(0, i, 0);
				}

				for (var i = 50; i >= 0; i -= increment)
				{
					SetPwmRgb(0, i, 0);
				}

				SetPwmRgb(0, 0, 0);

				Thread.Sleep(PulseGreenThreadSleep);

				index++;

				if (index >= 5 && autoKillThread)
					_killThread = true;
			}

			SetPwmRgb(0, 0, 0);
		}

		private static void PulseRed(double normalizedHealth, bool autoKillThread = true)
		{
			// assume .8 health
			// .2 * 20 = 4, lots of steps
			// assume lets say .2 health left
			// .8 * 20 gives a few steps
			// fewer steps, faster animation
			var increment = (int)((1 - normalizedHealth) * 30);

			// assume .8 health
			// (500 * .8) / 2 = 200ms sleep per cycle
			// assume .2 health
			// (500 * .2) / 2 = 50ms
			var sleep = (int)((500 * normalizedHealth) / 3.0);
			var index = 0;

			while (!_killThread)
			{
				BasePulseRed(increment);
				Thread.Sleep(sleep);
				index++;

				if (index >= 5 && autoKillThread)
					_killThread = true;
			}

			SetPwmRgb(0, 0, 0);
		}

		private static void BasePulseRed(int increment)
		{
			for (var i = 0; i <= 75; i += increment)
			{
				SetPwmRgb(i, 0, 0);
			}

			for (var i = 75; i >= 0; i -= increment)
			{
				SetPwmRgb(i, 0, 0);
			}

			SetPwmRgb(0, 0, 0);
		}

		private static void AbortAnimationThread()
		{
			_killThread = true;

			try
			{
				if(_animationThread != null)
					_animationThread.Abort();
			}
			catch (Exception ex0)
			{
				Debug.Print(ex0.ToString());
				Debug.Print("Thread still alive: ");
				Debug.Print("Killed Thread");
			}

			_killThread = false;
		}

		private static void SetPwmRgb(int red, int green, int blue)
		{
			// typically, 0 == off and 100 is on
			// things flipped however for the lighting so building this in.

			red = MaxValue - red;
			green = MaxValue - green;
			blue = MaxValue - blue;

			red = CheckBound(red, MaxValue);
			green = CheckBound(green, MaxValue);
			blue = CheckBound(blue, MaxValue);

			RedPwm.SetDutyCycle((uint) red);
			GreenPwm.SetDutyCycle((uint) green);
			BluePwm.SetDutyCycle((uint) blue);

			Thread.Sleep(ThreadSleep);
		}

		public static int CheckBound(int value, int max)
		{
			return CheckBound(value, 0, max);
		}

		public static int CheckBound(int value, int min, int max)
		{
			if (value <= min)
				value = min;
			else if (value >= max)
				value = max;

			return value;
		}
	}
}